package com.workcell.workdata.im.component.function;

import com.eatthepath.pushy.apns.ApnsClient;
import com.eatthepath.pushy.apns.ApnsClientBuilder;
import com.eatthepath.pushy.apns.ApnsPushNotification;
import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;
import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
import com.eatthepath.pushy.apns.util.TokenUtil;
import com.farsunset.cim.model.Message;
import com.workcell.workdata.im.component.event.APNsCloseEvent;
import com.workcell.workdata.im.component.event.APNsOpenEvent;
import com.workcell.workdata.im.component.event.LogoutEvent;
import com.workcell.workdata.im.component.event.ThirdMessagePushEvent;
import com.workcell.workdata.im.component.redis.KeyValueRedisTemplate;
import com.workcell.workdata.im.configuration.properties.APNsProperties;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;


@Component
@ConditionalOnProperty(name = {"hoxin.apns.enable"})
public class APNsPushHandler{

    private static final String APNS_DEVICE_TOKEN = "APNS_TOKEN_%s";

    private static final String LOCK_KEY = "APNS_PUSH_LOCK_%s";

    private final Logger logger = LoggerFactory.getLogger(APNsPushHandler.class);

    private final ApnsClient apnsClient;

    private final APNsProperties properties;

    @Resource
    private KeyValueRedisTemplate keyValueRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private APNsAlertHelper apNsAlertHelper;


    @Autowired
    public APNsPushHandler(APNsProperties properties) throws IOException {

        this.properties = properties;

        InputStream stream = getClass().getResourceAsStream(properties.getP12File());

        apnsClient = new ApnsClientBuilder()
                .setApnsServer(properties.isDebug() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
                .setClientCredentials(stream, properties.getP12Password())
                .build();
    }


    @Async("apnsTaskExecutor")
    @EventListener(classes = ThirdMessagePushEvent.class,condition = "#root.args[0].isNotEmpty")
    public void accept(ThirdMessagePushEvent event){

        String key = String.format(LOCK_KEY, event.getTraceId());
        RLock lock = redissonClient.getLock(key);

        if (!lock.tryLock()) {
            return;
        }

        try {
            event.getMessageList().forEach(APNsPushHandler.this::accept);
        } finally {
            lock.unlock();
        }

    }

    private void accept(Message message) {

        String deviceToken = keyValueRedisTemplate.get(String.format(APNS_DEVICE_TOKEN,message.getReceiver()));

        if(StringUtils.isBlank(deviceToken)) {
            return ;
        }

        ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();

        payloadBuilder.setAlertTitle(apNsAlertHelper.getTitle(message));
        payloadBuilder.setAlertBody(apNsAlertHelper.getBody(message));

        payloadBuilder.setSound("default");
        payloadBuilder.setBadgeNumber(1);
        payloadBuilder.addCustomProperty("id",message.getId());
        payloadBuilder.addCustomProperty("action",message.getAction());
        payloadBuilder.addCustomProperty("content",message.getContent());
        payloadBuilder.addCustomProperty("sender",message.getSender());
        payloadBuilder.addCustomProperty("receiver",message.getReceiver());
        payloadBuilder.addCustomProperty("format",message.getFormat());
        payloadBuilder.addCustomProperty("extra",message.getExtra());
        payloadBuilder.addCustomProperty("timestamp",message.getTimestamp());

        String token = TokenUtil.sanitizeTokenString(deviceToken);

        String payload = payloadBuilder.build();

        ApnsPushNotification notification = new SimpleApnsPushNotification(token, properties.getAppId(), payload);

        apnsClient.sendNotification(notification).whenComplete((response, cause) -> {
            if (response != null) {
                logger.info("APNs push done.\ndeviceToken : {} \napnsPayload : {}",deviceToken,payload);
            } else {
                logger.error("APNs push failed",cause);
            }
        });
    }

    @EventListener(classes = APNsOpenEvent.class)
    public void onAPNsOpen(APNsOpenEvent event){
        keyValueRedisTemplate.set(String.format(APNS_DEVICE_TOKEN,event.getUid()),event.getDeviceToken());
    }

    @EventListener(classes = APNsCloseEvent.class)
    public void onAPNsClose(APNsCloseEvent event){
        keyValueRedisTemplate.delete(String.format(APNS_DEVICE_TOKEN,event.getUid()));
    }

    @EventListener(classes = LogoutEvent.class)
    public void onLogout(LogoutEvent event){
        keyValueRedisTemplate.delete(String.format(APNS_DEVICE_TOKEN,event.getUid()));
    }



}
